#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import time
import logging
import dns.resolver
from typing import List, Set
import json
import os

from alibaba_cloud_sg import AlibabaCloudSecurityGroupManager
from alibaba_cloud_swas import AlibabaCloudSWASManager
from tencent_cloud_sg import TencentCloudSecurityGroupManager

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

DESCRIPTION = "Auto-synced rule by domain-ip-syncer"

class DomainIPSyncer:
    def __init__(self, config_file: str = "config.json"):
        """
        初始化域名IP同步器
        
        Args:
            config_file: 配置文件路径
        """
        self.config = self._load_config(config_file)
        self.previous_ips = set()
        
        # 初始化云服务管理器
        alibaba_config = self.config["alibaba_cloud"]
        self.alibaba_manager = AlibabaCloudSecurityGroupManager(
            alibaba_config["access_key_id"],
            alibaba_config["access_key_secret"],
            alibaba_config["region_id"]
        )
        
        # 初始化阿里云轻量应用服务器管理器
        self.alibaba_swas_manager = AlibabaCloudSWASManager(
            alibaba_config["access_key_id"],
            alibaba_config["access_key_secret"],
            alibaba_config["region_id"]
        )
        
        tencent_config = self.config["tencent_cloud"]
        self.tencent_manager = TencentCloudSecurityGroupManager(
            tencent_config["secret_id"],
            tencent_config["secret_key"],
            tencent_config["region"]
        )
        
    def _load_config(self, config_file: str) -> dict:
        """
        加载配置文件
        
        Args:
            config_file: 配置文件路径
            
        Returns:
            配置字典
        """
        # 默认配置
        default_config = {
            "domains": ["domain1.com", "domain2.com"],
            "check_interval": 300,
            "alibaba_cloud": {
                "access_key_id": "",
                "access_key_secret": "",
                "region_id": "cn-hangzhou",
                "security_group_id": "",
                "swas_instances": []  # 轻量应用服务器实例列表
            },
            "tencent_cloud": {
                "secret_id": "",
                "secret_key": "",
                "region": "ap-beijing",
                "security_group_id": ""
            },
            "port_range": "1/65535"  # 默认开放所有端口
        }
        
        # 如果配置文件存在，则加载配置文件
        if os.path.exists(config_file):
            with open(config_file, 'r') as f:
                user_config = json.load(f)
                default_config.update(user_config)
        
        # 从环境变量加载敏感信息（如果存在）
        default_config["alibaba_cloud"]["access_key_id"] = os.environ.get(
            "ALIBABA_CLOUD_ACCESS_KEY_ID", 
            default_config["alibaba_cloud"]["access_key_id"]
        )
        default_config["alibaba_cloud"]["access_key_secret"] = os.environ.get(
            "ALIBABA_CLOUD_ACCESS_KEY_SECRET", 
            default_config["alibaba_cloud"]["access_key_secret"]
        )
        default_config["tencent_cloud"]["secret_id"] = os.environ.get(
            "TENCENT_CLOUD_SECRET_ID", 
            default_config["tencent_cloud"]["secret_id"]
        )
        default_config["tencent_cloud"]["secret_key"] = os.environ.get(
            "TENCENT_CLOUD_SECRET_KEY", 
            default_config["tencent_cloud"]["secret_key"]
        )
        
        return default_config
    
    def _resolve_domain_ips(self, domains: List[str]) -> Set[str]:
        """
        解析域名获取IPv4地址
        
        Args:
            domains: 域名列表
            
        Returns:
            IP地址集合
        """
        ips = set()
        resolver = dns.resolver.Resolver()
        
        for domain in domains:
            try:
                answers = resolver.resolve(domain, 'A')
                for rdata in answers:
                    ips.add(rdata.address)
                    logger.info(f"域名 {domain} 解析到IP: {rdata.address}")
            except Exception as e:
                logger.error(f"解析域名 {domain} 失败: {e}")
                
        return ips
    
    def _sync_to_alibaba_cloud(self, ips: Set[str]):
        """
        同步IP到阿里云安全组和轻量应用服务器防火墙
        
        Args:
            ips: IP地址集合
        """
        alibaba_config = self.config["alibaba_cloud"]
        
        # 同步到传统ECS安全组（如果配置了安全组ID）
        if alibaba_config["security_group_id"]:
            try:
                self.alibaba_manager.sync_rules(
                    alibaba_config["security_group_id"],
                    ips,
                    self.config["port_range"],
                    DESCRIPTION
                )
                logger.info("阿里云安全组同步完成")
            except Exception as e:
                logger.error(f"阿里云安全组同步失败: {e}")
        else:
            logger.warning("未配置阿里云安全组ID，跳过安全组同步")
            
        # 同步到轻量应用服务器防火墙（如果有配置实例）
        if alibaba_config["swas_instances"]:
            for instance in alibaba_config["swas_instances"]:
                if instance.get("enabled", True):  # 默认启用
                    try:
                        self.alibaba_swas_manager.sync_rules(
                            instance["instance_id"],
                            ips,
                            self.config["port_range"],
                            DESCRIPTION
                        )
                        logger.info(f"阿里云轻量应用服务器 {instance['instance_id']} 防火墙同步完成")
                    except Exception as e:
                        logger.error(f"阿里云轻量应用服务器 {instance['instance_id']} 防火墙同步失败: {e}")
        else:
            logger.info("未配置阿里云轻量应用服务器实例，跳过轻量应用服务器防火墙同步")
        
    def _sync_to_tencent_cloud(self, ips: Set[str]):
        """
        同步IP到腾讯云安全组
        
        Args:
            ips: IP地址集合
        """
        tencent_config = self.config["tencent_cloud"]
        if not tencent_config["security_group_id"]:
            logger.warning("未配置腾讯云安全组ID，跳过同步")
            return
            
        # 转换端口范围格式以适应腾讯云
        port_range = self.config["port_range"]
        if port_range == "1/65535":
            port_range = "ALL"
            
        try:
            self.tencent_manager.sync_rules(
                tencent_config["security_group_id"],
                ips,
                port_range,
                DESCRIPTION
            )
            logger.info("腾讯云安全组同步完成")
        except Exception as e:
            logger.error(f"腾讯云安全组同步失败: {e}")
    
    def sync_once(self):
        """
        执行一次同步操作
        """
        logger.info("开始执行IP同步...")
        
        # 解析域名获取当前IP
        current_ips = self._resolve_domain_ips(self.config["domains"])
        logger.info(f"当前解析到的IP: {current_ips}")
        
        # 如果IP没有变化，则不执行任何操作
        if current_ips == self.previous_ips:
            logger.info("IP地址无变化，无需更新安全组规则")
            return
            
        # 如果IP有变化，同步到云服务商安全组
        logger.info(f"检测到IP变化，旧IP: {self.previous_ips}，新IP: {current_ips}")
        
        # 同步到阿里云
        self._sync_to_alibaba_cloud(current_ips)
        
        # 同步到腾讯云
        self._sync_to_tencent_cloud(current_ips)
        
        # 更新历史记录
        self.previous_ips = current_ips.copy()
        logger.info("IP同步完成")
    
    def run(self):
        """
        运行主循环
        """
        logger.info("启动域名IP同步服务...")
        logger.info(f"监控域名: {self.config['domains']}")
        logger.info(f"检查间隔: {self.config['check_interval']} 秒")
        
        while True:
            try:
                self.sync_once()
            except Exception as e:
                logger.error(f"同步过程中发生错误: {e}")
            
            # 等待下次检查
            time.sleep(self.config["check_interval"])


if __name__ == "__main__":
    syncer = DomainIPSyncer()
    syncer.run()